package SSF.OS.TCP;
/**
 * Send_WndManager.java
 *
 * Created: Sat Feb 27 21:20:17 1999 hongbol
 * Revision Sat Nov 20 1999 ato
 * Modified by hongbol 1/14/2001
 * @version 1.3
 */
import com.renesys.raceway.SSF.*;
import SSF.OS.*;
import SSF.OS.Socket.*;
import java.util.*;
import java.io.*;

/**
 * Send window manager manages sending data messages and receives ACKs.
 * It maintains a send queue and the send window parameters, calculates 
 * RTTs, implements fast retransmission, fast recovery and slow start 
 * algorithms.
 */
public class Send_WndManager
{  
  int round = 0;

  /** 
   * Reference to the owner of this send window manager.
   */
  tcpSession owner;
   
  /**
   * Initial send sequence number.
   */
  public long InitSEQno;

  /**
   * The highest sequence number that has been sent.
   */
  public long LastSent;

  /**
   * The sequence number to be send next.
   */
  public long SendNext;

  /**
   * The sequence number of the last packet written by application
   * (reserved).
   */
  public long LastWritten;

  /**
   * The first unacknowledged sequence number.
   */
  public long FirstUnACKed;;

  /*
   * the effective window size of the packets can be send
   */
  int EffectiveWnd;

  /**
   * Advertised Window Size of Receiver contained in ACK message.
   */
  public int AdvertisedWnd;

  /**
   * Send Queue for this TCP session.
   */
  public SendQueue SendBuffer;

  public int maxSendWnd;

  /** the maximum segment size of one TCP packet*/
  public int MSS ;

  /** retransmission control variable: log rexmt exp. backoff, current timeout
   * and consecutive duplicate ACKs received 
   */
  private int rexShift;
  int rexTimeout;
  private int MaxRexTimes;
  private short rexDupacks;
  private long dupACKno;
  private long rexACKno;

  /* 
   * congestion control variables (slow start, source quench, rexmit after
   * loss): 
   */
  /**
   * congestion control window size (cnwd in BSD) 
   */
  int conWnd;

  /**
   * maximum congestion control window size (default to receiver window 
   * in BSD) 
   */
  int MaxConWnd;

  /** threshhold of the control window size ( called ssthresh in BSD)*/
  int conThresh;

  /**  RTT measured each time */
  int Mrtt;

  /** RTT used for caculation and estimation */
  int rtt;

  /**
   * the sequence number of the packet used to calculate RTT
   */ 
  long rttSeq;

  /**
   * Smoothed RTT time
   */
  int rttSmoothed;

  /**
   * Variance of RTT
   */
  int rttVar;
  /**
   * Indicate if one round trip is finished and a new round trip can begin
   */ 
  boolean rttFinished = true;

  /** the send time of the packet which will be used to caculate RTT*/
  private long sendTime;


  /** indicate if a FIN message has been put into the send queue*/
  public boolean FINsent = false;

  /** 
   * the seqence number the FIN message used to identify the 
   * acknowledgement of a FIN message
   */
  long FINSeq;

  /* constants used to calculate RTT */
  static final short TCP_RTT_SCALE = 32;
  static final short TCP_RTT_SHIFT = 3;
  static final short TCP_RTTVAR_SCALE = 16;
  static final short TCP_RTTVAR_SHIFT = 2;
  static final short TCP_DELTA_SHIFT = 2;

  /** calculate RTO by scaled value */
  public int  TCP_REXMTVAL(){
      return  ((rttSmoothed >> TCP_RTT_SHIFT)  
          + rttVar);
  }
  
  /**  how many TCP slow timeout in 1 seconds */
  int TCP_SLOWHZ; 

  /** maximum RTO value*/
  int TCP_REXMTMAX; // = 64 * TCP_SLOWHZ;

  /** minimum RTO value*/
  int minRtt; // 1 * TCP_SLOWHZ;

  /** the maximum times a packet can be retransmitted*/
  int TCP_MAXRXTSHIFT;//set to 12 byte default

  /** Retransmission timeout backoff value*/
  static int tcp_backoff[] =
    { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };

  /** retransmission timeout tick counter */
  int rexTimerCnt; 

  boolean DEBUG;
  static final short RTT_DUMP = 1;//flags for different dump requirement
  static final short RTO_DUMP = 2;
  static final short CWND_DUMP = 3;

  PrintWriter rttWriter = null; //print writers for each dump file
  PrintWriter rtoWriter = null;
  PrintWriter cwndWriter = null;

  /**
   * flags for fast recovery algorithm and TCP Tahoe implementation
   */
  boolean FAST_RECOV;

  /** Sender side substate */
  int sub_state;//0 -- unknown state, 1 -- slow start, 
                //2 -- congestion avoidance, 3 -- fast recovery 

  /**
   * Initialize the send window parameters
   */
  public Send_WndManager(tcpSession sessHlp, tcpSessionMaster init) {
    String str;
    owner = sessHlp;
    DEBUG = owner.DEBUG;
    LastWritten = 1;
    TCP_SLOWHZ = (int)(SSF.Net.Net.seconds(1)/init.TCP_SLOW_INTERVAL);
    rexDupacks = 0;
    EffectiveWnd = 0;
    AdvertisedWnd = 1;
    rtt = 0;
    rttSmoothed = 0;
    /* set init rto to 6.0 sec*/
    rttVar = (int)(3.0 *TCP_SLOWHZ)<<TCP_RTTVAR_SHIFT;
    rexTimeout = ((rttSmoothed >>2) + rttVar)>>1;
    TCP_REXMTMAX = 64 * TCP_SLOWHZ;
    minRtt = 1 * TCP_SLOWHZ;
    rttSeq = InitSEQno = init.ISS;
    LastSent = InitSEQno - 1;
    FirstUnACKed = InitSEQno;
    SendNext = InitSEQno;
    conWnd = MSS = init.MSS;
    TCP_MAXRXTSHIFT = init.MaxRexmitTimes;
    conThresh = 65536; //64k for any implement
    SendBuffer = new SendQueue(MSS, init.SendBufferSize);
    maxSendWnd = init.SendWndSize * MSS;
    if(DEBUG) try {
      if((str = init.rttDumpFile) != null) {
        str = str + "_" + ((SSF.Net.Host)init.inGraph()).nhi 
              + "_" + owner.flowID + ".out";
        rttWriter = new PrintWriter(new FileOutputStream(str));
        rttWriter.println("% "+owner.session_info);
      }
      if((str = init.rexDumpFile) != null) {
        str = str + "_" + ((SSF.Net.Host)init.inGraph()).nhi 
              + "_" + owner.flowID + ".out";
        rtoWriter = new PrintWriter(new FileOutputStream(str));
        rtoWriter.println("% "+owner.session_info);
      }
      if((str = init.cwndDumpFile) != null) {
        str = str + "_" + ((SSF.Net.Host)init.inGraph()).nhi 
              + "_" + owner.flowID + ".out";
        cwndWriter = new PrintWriter(new FileOutputStream(str));
        cwndWriter.println("% "+owner.session_info);
      }
    } catch (IOException ioex) {
      ioex.printStackTrace(); 
    }

    MaxConWnd = init.MaxConWnd * MSS;
    FAST_RECOV = ((init.tcp_opt & init.FAST_RECOVERY) != 0);
    sub_state = 0;
  }

  //transform tick to second
  double tickToSeconds(int tick){
    return ((double)tick/(double)TCP_SLOWHZ);
  }

  //get function used by Instrumentation class
  String getSendWndParam(){
    String str = " " + LastSent + " " + SendNext + " " + (FirstUnACKed - 1) + 
      " " + SendBuffer.len + " " + AdvertisedWnd + " " + rexDupacks +
      " " + conWnd + " " + conThresh + 
      " " + rttSeq + " " + tickToSeconds(Mrtt) + 
      " " + tickToSeconds(rttSmoothed>> TCP_RTT_SHIFT) + 
      " " + tickToSeconds(rexTimeout) + " " + rexTimerCnt;
    return str;
  }

  //function used to dump internal TCP parameters 
  void dump(short type, TCP_Message msg){
    float time = owner.getSimTime();
    switch (type) {
    case RTT_DUMP:
      {
        int is_ack;
        if(rttWriter != null){
          if ((msg.flags & TCP_Header.SYN) != 0){
            is_ack = 0;
          }
          else{
            is_ack = ((msg.flags & TCP_Header.ACK) != 0)? 1 : 0;
          }
          rttWriter.println(is_ack + " " + time  
                            + " " + tickToSeconds(Mrtt) 
                            + " " + tickToSeconds(rexTimeout)
                            + " " + owner.flowID);
          rttWriter.flush();
        }
      }
      break;
    case RTO_DUMP:
      if(rtoWriter != null){
        rtoWriter.println(time + " " + tickToSeconds(Mrtt) 
                          + " " + tickToSeconds(rttSmoothed >> TCP_RTT_SHIFT)
                          + " " + tickToSeconds(rexTimeout) 
                          + " " + rexTimerCnt
                          + " " + owner.flowID);
        rtoWriter.flush();
      }
      break; 
    case CWND_DUMP:
      if(cwndWriter != null){
        cwndWriter.println(time + " " + conWnd 
                           + " " + conThresh 
                           + " " + AdvertisedWnd
                           + " " + owner.flowID);
        cwndWriter.flush();
      }
      break; 
    default:
      break;
    }   // end of switch (type) 

  }

  /**
   * Process ACKs, if not duplicate ACKs cancel the retransmission timer 
   * otherwise retransmits the lost packet. Calculate congestion control
   * window size, set the threshold parameter of the Fast recovery and 
   * Slow start algorithm.
   * @param message the ACK message need to process.
   */
  public void Rcv_Ack_Process(TCP_Message msg){
    if(msg.ACKno > (LastSent + 1))return; 
    //System.out.println("Rcv_Ack_Process FirstUnACKed= "+FirstUnACKed);
    if(msg.ACKno > FirstUnACKed){

      if((rexDupacks >= 3) && (conWnd > conThresh)) {
        conWnd = conThresh; // fast recovery finished
        sub_state = 2;      // congestion avoidance
      }

      rexDupacks = 0;

      SendItem FirstUnAckedPkt = SendBuffer.dummyItem.next;
      SendItem tmp;
      while(msg.ACKno > FirstUnACKed ) {

        /*
         * if all pakckets outside has been acked, cancel rexit timer
         * else set rexit timer to current timeout value
         *
         */
        if(msg.ACKno == (LastSent + 1)) {
          rexTimerCnt = 0;
        } else {
          rexTimerCnt = rexTimeout;
        }

        if(DEBUG)
          dump(RTO_DUMP,msg);

        /* If the ACK of packet with rttSeq is received caculate current RTT. 
         * And if the packet is a retransmiting one, don't caculate current 
         * RTT.
         */ 
        if(FirstUnAckedPkt.seqno() == rttSeq){
          if (rtt != 0){
            
            Mrtt = rtt - 1;
            calc_RTT();       // use rtt counted by slow timer
            if(DEBUG)
              dump(RTT_DUMP,msg);
            rtt = 0;
          } else{
            // for dump message only
            Mrtt = -1;
            if(DEBUG)
              dump(RTT_DUMP,msg);
          }
        }

        // FIN is ACKed
        // System.out.println("Rcv_Ack_Process FirstUnAckedPkt.seqno()= "+FirstUnAckedPkt.seqno()
        //                    +" FINsent= "+FINsent+" ACKFIN= "+owner.rcv.ACKFINreceived);
        if(FINsent && (FirstUnAckedPkt.seqno() == FINSeq)){
         
          if (owner.rcv.ACKFINreceived ) {
            owner.CloseStateTransfer3();
          } else {   
            owner.CloseStateTransfer2();
            //System.out.println("Rcv_Ack_Process Transfer2= "+owner.tcp_state);
          }   
        }

        FirstUnACKed = FirstUnAckedPkt.nextseqno();
        tmp = FirstUnAckedPkt;
        int ackedlen = ((TCP_Message)tmp.msg).seglen;
        FirstUnAckedPkt = FirstUnAckedPkt.next;

        // NOTE: remove() must be before dataAcked() - Continuation
        SendBuffer.remove(tmp);

        // notify tcpSession that data was acked, ato 5/28/01
        // if write concluded, a Continuation.success() will fire
        //owner.dataAcked(((TCP_Message)tmp.msg).seglen);
        owner.dataAcked(ackedlen);

      } // end of while

      /** if SendNext had been moved back, means an ACK for a retransmission;
       *  move SendNext to the ACK number. It's important for effective
       *  window.
       */
      if(SendNext < msg.ACKno)
        SendNext = msg.ACKno;

      /* if the ACK is not for SYN message, then increase congestion window */
      if(msg.ACKno != owner.ISS + 1){
        /* calc conWnd*/
        if(conWnd < conThresh){ // slow start increase exponentially
          conWnd += MSS;
          sub_state = 1;
        }else {                 // congestion avoidance
          conWnd += MSS * MSS / conWnd;
          sub_state = 2;
        }   // end of else
      }
    }

    /* duplicate ack tests:
     * 1.If the ackno is less than or equal to send unackno, and
     * 2. the lenght of the received segment is 0, and 
     * 3, the advertised window has not changed, and
     * 4. TCP has outstanding data that has not been acked(rexTimerCnt != 0)
     * 5. the received segment contains the biggest ACK TCP has seen(ACKno == FirstUnACKed)
     */
    else if (msg.ACKno <= FirstUnACKed){ //possible duplicate acks
      if((msg.seglen == 0)
         && (AdvertisedWnd == msg.AdvertisedWnd)) //conforms to conditions 1,2,3 
        {
          dupACKno = msg.ACKno;
          if((rexTimerCnt == 0) || (msg.ACKno != FirstUnACKed))
            rexDupacks = 0;                      // does not conform to conditions 4,5
          else if(++rexDupacks == 3){ //conform to all condition
            Dup_Ack_Process();
            if(DEBUG)
              dump(RTO_DUMP,msg);
            Send_Data_Process();  
          } else if(rexDupacks > 3){ //conform to all condition
            if(FAST_RECOV){
              conWnd += MSS; 
              sub_state = 3;
            }
            Send_Data_Process();    
          }   
        } else {
          rexDupacks = 0;
        }
    }
    AdvertisedWnd = msg.AdvertisedWnd;

    /* if send buffer has enough space, wake up application's write process*/
    if((SendBuffer.space() > SendBuffer.TCP_BUFFER_SPACE/2)
       || (SendBuffer.length() < AdvertisedWnd))
      owner.dataFeed();
    Send_Data_Process();
    if(DEBUG)
      dump(CWND_DUMP,msg);
  }


  /**
   * process called when receiving the 3rd duplicated ACKs, implement 
   * fast retransmission and fast recovery
   */
  public void Dup_Ack_Process(){
    //fast recovery
    calc_ssthresh();

    //Fast rexmit process
    SendItem FirstUnAckedPkt = SendBuffer.dummyItem.next;
    rexTimerCnt = 0;
    rtt = 0;
    if(dupACKno <= FirstUnAckedPkt.seqno()){
      FirstUnAckedPkt.rexTimes++;
      rexTimerCnt = rexTimeout;
      conWnd = MSS;
      sub_state = 1;
      try {
          //copy TCP message and its payload
          TCP_Message tcpMsg = new TCP_Message((TCP_Message)FirstUnAckedPkt.msg);
          FirstUnAckedPkt.msg = tcpMsg;
          if(!FAST_RECOV){ //implement Tahoe TCP
            SendBuffer.currentItem = FirstUnAckedPkt;
            SendNext = tcpMsg.SEQno;
          }
	  owner.rcv.setACK(tcpMsg);

          if(DEBUG){
            System.out.println(owner.sessionInfo("Fast retransmit:"));
            tcpMsg.printMsg();
          }
          (owner.ipsess).push(tcpMsg.ipHeader(),owner);
          FirstUnAckedPkt.setPrevSendTime(owner.getSimTime());

          if(!FAST_RECOV) //implement Tahoe TCP
            SendNext += (FirstUnAckedPkt.nextseqno() 
                         - FirstUnAckedPkt.seqno());

      } catch (ProtocolException e) {
        System.err.println("TCP: " + e);
      }
      if(FAST_RECOV){
        conWnd = conThresh + 3 * MSS;   // fast recovery
        sub_state = 3;
      }
      rexACKno =FirstUnAckedPkt.nextseqno();
    }
  }

  /** check if the idle time is longer enough for a slow start*/
  boolean checkIdle(){

    /* if we have been idle for "a while" and no acks are
     * expected to clock out any data we send --
     * slow start to get ack "clock" running again.
     */
     
    if((owner.idleTime >  rexTimeout)
       && (LastSent == FirstUnACKed - 1)){
      conWnd = MSS;
      sub_state = 1;
      return true;
    }else {
      return false;
    } 

  }
  /**
   * calculate the send window size and send that number of bytes of data if
   * send queue has enough data to be sent or send out whatever in the send
   * queue, set retransmission timer for each packet.
   */
  public void Send_Data_Process(){

    if(!SendBuffer.hasMore()) return;

    calc_EffectiveWnd();
    int ByteSent = 0;
    SendItem item;

    while(SendBuffer.hasMore()){
      
      ByteSent += SendBuffer.nextMsgLen();
      if(ByteSent > EffectiveWnd)
        break;
      
      item = SendBuffer.nextTosend();

      TCP_Message tcpMsg = (TCP_Message)item.msg ;

      if(item.seqno() > InitSEQno){ // retransmission caused by partial ACK
        try {
          tcpMsg = new TCP_Message((TCP_Message)item.msg);	
	  item.msg = tcpMsg;
          item.rexTimes++;
        } catch (ProtocolException e) {
          System.err.println("TCP: " + e);
        }
      } else {                       // new data with uninitialized SEQno
        item.setSEQno(LastSent + 1);
        LastSent += (item.nextseqno() - item.seqno());  //considering SYN & FIN
        // set up rtt measurement
        if(rtt == 0){ 
          sendTime = owner.tcpServ.inGraph().now();
          rttSeq = item.seqno();
          if(DEBUG)
            dump(RTT_DUMP, (TCP_Message)item.msg);
          rtt = 1;
        }
        if(FINsent && (tcpMsg.flags == TCP_Header.FIN)){
          FINSeq = item.seqno();
          owner.tcpServ.ISS = FINSeq + owner.tcpServ.ISS_INCR;
          owner.CloseStateTransfer2(); // sets state = FIN_WAIT_1
          //System.out.println("Send_Data_Process FINseq= "+FINSeq+" Transfer2= "+owner.tcp_state);
        }
        if(DEBUG)
          if(LastSent == InitSEQno)
            dump(CWND_DUMP,tcpMsg);
      
      }   // end of else
 
      try {
	owner.rcv.setACK(tcpMsg);
        owner.ipsess.push(tcpMsg.ipHeader(),owner);
        SendNext += (item.nextseqno() - item.seqno());
        item.setPrevSendTime(owner.getSimTime()); 
      } catch (ProtocolException e) {
        System.err.println("TCP: " + e);
      }

      //set rexmit timer to current timeout value
      if(rexTimerCnt == 0){
        rexTimerCnt = rexTimeout;
        if(DEBUG)
          dump(RTO_DUMP, (TCP_Message)item.msg);
      }
    }
      
  }


  int rangeSet(int calcValue, int minValue, int maxValue){
    if(calcValue <= minValue)calcValue = minValue;
    if(calcValue >= maxValue)calcValue = maxValue;
    return calcValue;                 
  }

  /**
   * Retransmition process when rexit timer expires.
   * Tests if the number of consecutive retransmissions reached the maximum
   * of TCP_MAXRXTSHIFT = 12; if yes drop the connection.<P>
   * With Karn's algorithm, two retransmissions are considered consecutive
   * (see <TT>rexShift</TT>) if there was no RTT calculation between
   * these two retransmissions. In other words if a segment was retransmitted,
   * and either the same segment is retransmitted again, or the next segment
   * is again retransmitted, these are two consecutive retransmissions.<P>
   * Thus a case of the 12 consecutive retransmissions may occur when
   * the same segment is retrasmitted 12 times; but also when
   * 12 consecutive segments are sent and each of them is retransmitted once, etc.<P>
   * Otherwise, enter slow start.
   */
  public void Rexmit(){
    SendItem rex_item = SendBuffer.dummyItem.next;
    
      try {
        rex_item.rexTimes++;
        rexShift++;
        if (rexShift >= TCP_MAXRXTSHIFT) {
          if(owner.INFO)
            System.err.println(owner.sessionInfo("retransmitted " +
                       rex_item.rexTimes + " times, connection dropped!"));
          owner.drop(socketMaster.ETIMEDOUT);
          return;
        }

        /* update rttVar */
        if (rexShift > TCP_MAXRXTSHIFT / 4) {

          rttVar += (rttSmoothed >> TCP_RTT_SHIFT);
          rttSmoothed = 0;
        }

        rexTimeout = rangeSet(TCP_REXMTVAL() * tcp_backoff[rexShift], 
                              minRtt, TCP_REXMTMAX);
        rexTimerCnt = rexTimeout;

	/*set congestion threshold */
	calc_ssthresh();
	conWnd = MSS;//slow start

        rexDupacks = 0;
        rtt = 0;
        
        //copy TCP message and its payload
        TCP_Message tcpMsg = new TCP_Message((TCP_Message)rex_item.msg);
	rex_item.msg = tcpMsg;                 //release the old message
	owner.rcv.setACK(tcpMsg);

        /* move the pointer back to the retransmitted packet */
        SendBuffer.currentItem = rex_item;
        SendNext = rex_item.seqno();
        if(DEBUG) {
          dump(RTO_DUMP,tcpMsg);
          dump(CWND_DUMP,tcpMsg);
        }
        owner.ipsess.push(tcpMsg.ipHeader(),owner);
        SendNext += (rex_item.nextseqno() - rex_item.seqno());
        rex_item.setPrevSendTime(owner.getSimTime());
        if(owner.DEBUG){
          System.out.println(owner.sessionInfo("Retransmission " 
                             + rex_item.rexTimes + " times"));   
          ((TCP_Message)rex_item.msg).printMsg();
        }
      } catch (ProtocolException e) {
        System.err.println("TCP: " + e);
      }
  }

  /** 
   * Set slow start thresh to the half of the flight size when loss begins,
   * min(conWnd, AdvertisedWnd)/2, but no less than 2*MSS.
   */
  public void calc_ssthresh(){
    int win;
    if(AdvertisedWnd < conWnd)
      win = AdvertisedWnd/2/MSS;
    else {
      win = conWnd/2/MSS;
     }   // end of else 
    if(win < 2) win = 2;
    conThresh = win * MSS;
  }

  /**
   * Collect new round-trip time estimate
   * and update averages and current timeout.
   * (Ported from BSD TCP function tcp_xmit_timer())
   *
   * Jacobson algorithm for the calculation of the RTT value and Retransmition 
   * timeout value. M is measured RTT, A is smothed RTT, D is the smothed mean
   * deviation.<P> 
   *           Err = M - A<P>
   *           A <- A + g * Err            -- g = 0.125<P>
   *           D <- D + h * (|Err| - D)    -- h = 0.25<P>
   *           RTO = A + 4 * D
   */
     
  public void calc_RTT(){
    int delta;
    if(rttSmoothed != 0){
      /*
       * srtt is stored as fixed point with 5 bits after the
       * binary point (i.e., scaled by 8).  The following magic
       * is equivalent to the smoothing algorithm in rfc793 with
       * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
       * point).  Adjust rtt to origin 0.
       */
      delta = (rtt - 1) - (rttSmoothed >> TCP_RTT_SHIFT);
      
      if ((rttSmoothed += delta) <= 0)
        rttSmoothed = 1;

      /*
       * We accumulate a smoothed rtt variance (actually, a
       * smoothed mean difference), then set the retransmit
       * timer to smoothed rtt + 4 times the smoothed variance.
       * rttvar is stored as fixed point with 4 bits after the
       * binary point (scaled by 16).  The following is
       * equivalent to rfc793 smoothing with an alpha of .75
       * (rttvar = rttvar*3/4 + |delta| / 4).  This replaces
       * rfc793's wired-in beta.
       */
      if (delta < 0)
        delta = -delta;
      delta -= (rttVar >> TCP_RTTVAR_SHIFT);
      if ((rttVar += delta) <= 0)
        rttVar = 1;
    } else {
      /*
       * No rtt measurement yet - use the unsmoothed rtt.
       * Set the variance to half the rtt (so our first
       * retransmit happens at 3*rtt).
       */
      rttSmoothed = rtt << TCP_RTT_SHIFT;
      rttVar = rtt << (TCP_RTTVAR_SHIFT - 1);
    }
    rtt = 0;
    rexShift = 0;
    /*
     * the retransmit should happen at rtt + 4 * rttvar.
     * Because of the way we do the smoothing, srtt and rttvar
     * will each average +1/2 tick of bias.  When we compute
     * the retransmit timer, we want 1/2 tick of rounding and
     * 1 extra tick because of +-1/2 tick uncertainty in the
     * firing of the timer.  The bias will give us exactly the
     * 1.5 tick we need.  But, because the bias is
     * statistical, we have to test that we don't drop below
     * the minimum feasible timer (which is 2 ticks).
     */
     rexTimeout = rangeSet(TCP_REXMTVAL(), minRtt, TCP_REXMTMAX);
  }


  /**
   * Calculates the effective size of send window.
   */
  public int calc_EffectiveWnd() {
    int SendWnd = AdvertisedWnd;
    if(conWnd > MaxConWnd) conWnd = MaxConWnd;
    if(SendWnd > maxSendWnd) SendWnd = maxSendWnd;
    if(SendWnd > conWnd) SendWnd = conWnd;
    return EffectiveWnd = SendWnd - (int)(SendNext - FirstUnACKed);
  }
}



